/*
 * $Id: TaskListXmlBuilder.java,v 1.13 2010/01/08 16:29:31 afaichun Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.onwatch.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;

import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.simplx.xstream.JDomConverter;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.database.pojo.CfgOnwatchFileInfo;
import com.ge.healthcare.autosc.common.util.ASCFileHandler;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.ge.healthcare.autosc.onwatch.exception.CommandRefBuilderException;
import com.ge.healthcare.autosc.onwatch.exception.CommandRefBuilderFactoryException;
import com.ge.healthcare.autosc.onwatch.exception.TaskListXmlBuilderException;
import com.ge.healthcare.autosc.onwatch.pojo.OnwatchTaskInfo;
import com.ge.healthcare.autosc.onwatch.xml.tag.TaskListTag;
import com.ge.healthcare.autosc.onwatch.xml.tag.TaskRequestInfoTag;
import com.ge.healthcare.autosc.onwatch.xml.tag.TaskRequestTag;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
 * This class builds TaskList XML document
 * @author 212042946
 *
 */
public class TaskListXmlBuilder {

	private TaskListTag taskList = null;
	
	/**
	 * Constructor
	 * @param id
	 * @param usn
	 * @param mln
	 * @param iipVersion
	 */
	public TaskListXmlBuilder(Long id,
							String systemId,
							String iipVersion) {
		taskList = new TaskListTag(id, systemId, iipVersion);
	}
	
	/**
	 * Get TaskList XML document
	 * @return	TaskList XML String
	 */
	public String getTaskListXml() {
		XStream xStream = new XStream(new DomDriver());
		xStream.registerConverter(new JDomConverter());
		xStream.processAnnotations(TaskListTag.class);
		xStream.processAnnotations(TaskRequestTag.class);
		xStream.processAnnotations(TaskRequestInfoTag.class);
		return addDocType(xStream.toXML(taskList));
	}
	
	/**
	 * Add new Onwatch task to existing taskList
	 * @param onwatchTaskInfo
	 * @throws TaskListXmlBuilderException
	 */
	public void addNewTask(OnwatchTaskInfo onwatchTaskInfo) 
		throws TaskListXmlBuilderException {
		TaskRequestInfoTag taskRequestInfoTag = buildTaskInfo(onwatchTaskInfo);
		Element command = loadCommandContent(onwatchTaskInfo);
		ASCLogger.debug(this.getClass(), "addNewTask", "onwatchTaskId " + onwatchTaskInfo.getOnwatchTask().getOnwatchTaskId());
		TaskRequestTag taskRequestTag = 
			new TaskRequestTag(onwatchTaskInfo.getOnwatchTask().getOnwatchTaskId(),
							taskRequestInfoTag, command);
		taskList.addTaskRequest(taskRequestTag);
	}
	
	/*
	 * If the document type is defined, prepend to the first line
	 */
	private String addDocType(String taskList) {
		String newTaskList = taskList;
		String docType = ApplicationInfo.getInstance().getProperty(PropertyConstants.ONWATCH_TASKLIST_DOCTYPE);
		if(!ASCUtil.isNullOrEmpty(docType)) {
			newTaskList = docType + "\n" + newTaskList;
		}
		return newTaskList;
	}
	
	private TaskRequestInfoTag buildTaskInfo(OnwatchTaskInfo onwatchTaskInfo) {
		String user = onwatchTaskInfo.getUser();
		String requestor = onwatchTaskInfo.getRequestor();
		OnWatchLogger.addStepLog("user: " + user + ", requestor: " + requestor);
		TaskRequestInfoTag taskRequestInfoTag = new TaskRequestInfoTag(requestor, user);
		if(onwatchTaskInfo.isDebug()) {
			taskRequestInfoTag.markAsDebug();
		}
		return taskRequestInfoTag;
	}
	
	private Element loadCommandContent(OnwatchTaskInfo onwatchTaskInfo) 
		throws TaskListXmlBuilderException {
		String content = getCommandContent(onwatchTaskInfo);
		InputStream is = new ByteArrayInputStream(content.getBytes());
		Element commandElement = null;
		try {
			SAXBuilder builder = new SAXBuilder();
			Document commandDoc = builder.build(is);
			List<?> contents = commandDoc.getContent();
			if(contents.size() > 0) {
				commandElement = (Element)contents.get(0);
			}
		} catch (JDOMException je) {
			ASCLogger.error(this.getClass(), "loadCommandContent", "exception:", je);
			throw new TaskListXmlBuilderException("JDOMException while loading command content", je);
		} catch (IOException ioe) {
			ASCLogger.error(this.getClass(), "loadCommandContent", "exception:", ioe);
			throw new TaskListXmlBuilderException("IOException while load command content", ioe);
		}
		return commandElement;
	}
	
	private String substitution(OnwatchTaskInfo onwatchTaskInfo, String content) 
		throws TaskListXmlBuilderException {
		content = substituteByRef(content, onwatchTaskInfo);
		content = substituteByParam(content, onwatchTaskInfo);
		content = substituteByFile(content, onwatchTaskInfo);
		return content;
	}
	
	private String substituteByRef(String content, OnwatchTaskInfo onwatchTaskInfo)
		throws TaskListXmlBuilderException {
		//String methodName = "substituteByRef";
		try {
			// Keep looking for reference until no more reference can be found.
			ICommandRefBuilder builder = CommandRefBuilderFactory.getCommandRefBuilder(content);
			do  {
				if(builder != null) {
					content = builder.build(content, onwatchTaskInfo);
				} else {
					// no more reference
					break;
				}
			} while ((builder = CommandRefBuilderFactory.getCommandRefBuilder(content)) != null);	
		} catch (CommandRefBuilderFactoryException crbfe) {
			throw new TaskListXmlBuilderException("Failed creating command refernce builder", crbfe);
		} catch (CommandRefBuilderException crbe) {
			throw new TaskListXmlBuilderException("Failed performing command reference substitution", crbe);
		}
		return content;
	}
	
	private String substituteByParam(String content, OnwatchTaskInfo onwatchTaskInfo) 
		throws TaskListXmlBuilderException {
		String methodName = "substituteByParam";
		if(onwatchTaskInfo.hasDynamicArg()) {
			Map<String, String> requestProperties = onwatchTaskInfo.getRequestProperties();
			if(!ASCUtil.isNullOrEmptyMap(requestProperties)) {
				RE checkForVar;
				try {
					checkForVar = new RE("%PARAM:[A-Za-z0-9_-]+%");
					if(checkForVar.match(content)) {
						for(String name: requestProperties.keySet()) {
							RE nameSub = new RE("^(.*)%PARAM:" + name + "%(.*)$");
							while (nameSub.match(content)) {
								ASCLogger.info(this.getClass(), methodName,
										"Dynamic Substitute: %PARAM:" + name + "% with value: "
												+ requestProperties.get(name), true);
								content = nameSub.getParen(1)
										+ (String) requestProperties.get(name)
										+ nameSub.getParen(2);
							}
						}
					}
				} catch (RESyntaxException reException) {
					throw new TaskListXmlBuilderException(
							"Failed performing RE Conversion using requestProperties:"
									+ requestProperties, reException);
				}
			} else {
				ASCLogger.warn(this.getClass(), methodName, 
						"Onwatch task is configured to have dynamic content but " +
						"no dynamic data is provided. Ignore the dynamic flag. Task Id: " + 
						onwatchTaskInfo.getOnwatchTask().getTaskId());
			}
		}
		return content;
	}
	
	private String substituteByFile(String content, OnwatchTaskInfo onwatchTaskInfo)
		throws TaskListXmlBuilderException {
		String methodName = "substituteByFile";
		List<CfgOnwatchFileInfo> getFiles = onwatchTaskInfo.getGetFiles();
		List<CfgOnwatchFileInfo> putFiles = onwatchTaskInfo.getPutFiles();
		if(!ASCUtil.isNullOrEmptyList(getFiles)) {
			RE checkForVar;
			try {
				checkForVar = new RE("%GETFILE:[A-Za-z0-9_-]+%");
				if(checkForVar.match(content)) {
					for(CfgOnwatchFileInfo fileInfo: getFiles) {
						String fid = fileInfo.getFileId();
						RE nameSub = new RE("^(.*)%GETFILE:" + fid + "%(.*)$");
						while (nameSub.match(content)) {
							ASCLogger.info(this.getClass(), methodName,
									"Dynamic Substitute: %GETFILE:" + fid + "% with value: "
											+ fileInfo.getFileName(), true);
							content = nameSub.getParen(1)
									+ fileInfo.getFileName()
									+ nameSub.getParen(2);
						}
					}
				}
				checkForVar = new RE("%PUTFILE:[A-Za-z0-9_-]+%");
				if(checkForVar.match(content)) {
					for(CfgOnwatchFileInfo fileInfo: putFiles) {
						String fid = fileInfo.getFileId();
						RE nameSub = new RE("^(.*)%PUTFILE:" + fid + "%(.*)$");
						while (nameSub.match(content)) {
							ASCLogger.info(this.getClass(), methodName,
									"Dynamic Substitute: %PUTFILE:" + fid + "% with value: "
											+ fileInfo.getFileName(), true);
							content = nameSub.getParen(1)
									+ fileInfo.getFileName()
									+ nameSub.getParen(2);
						}
					}
				}
				
			} catch (RESyntaxException reException) {
				throw new TaskListXmlBuilderException(
						"Failed performing RE Conversion using request definition:"
								+ onwatchTaskInfo.getOnwatchTask().getRequestDefn(), reException);
			}
		}
		return content;
	}
	
	private String getCommandContent(OnwatchTaskInfo onwatchTaskInfo) 
		throws TaskListXmlBuilderException {
		File commandFile = onwatchTaskInfo.getCommandFile();
		OnWatchLogger.addStepLog("retrieve command file " + commandFile.getPath());
		String methodName = "getCommandContent";
		StringBuffer content = new StringBuffer();
		FileInputStream fis = null;
		BufferedReader reader = null;
		try {
			fis = new FileInputStream(commandFile);
			reader = new BufferedReader(new InputStreamReader(fis));
			
			String line = null;
			while((line = reader.readLine()) != null) {
				line = substitution(onwatchTaskInfo, line);
				content.append(line);
				content.append("\n");
			}
		} catch (FileNotFoundException fnfe) {
			String error = "Command file not found: " + commandFile.getPath();
			ASCLogger.error(this.getClass(), methodName, error, fnfe);
			throw new TaskListXmlBuilderException(error, fnfe);
		} catch (IOException ioe) {
			String error = "Error reading command file: " + commandFile.getPath();
			ASCLogger.error(this.getClass(), methodName, error, ioe);
			throw new TaskListXmlBuilderException(error, ioe);
		}finally{
			ASCFileHandler.closeInputStream(fis);
			ASCFileHandler.closeReader(reader);
		}
		return content.toString();
	}
}
